package com.hs.serial

import android.serialport.SerialPort
import com.hs.framework.toast.TipsToast
import com.hs.framework.utils.Speech
import java.io.File
import java.io.InputStream
import java.io.OutputStream
import java.util.concurrent.ArrayBlockingQueue

class SerialPortManager private constructor() {
    companion object {
        @Volatile
        private var instance: SerialPortManager? = null
        fun getInstance(): SerialPortManager {
            return instance ?: synchronized(this) {
                instance ?: SerialPortManager().also { instance = it }
            }
        }
    }

    private var receiverTask: Reading? = null
    private var sendTask: Task? = null
    private var serialPort: SerialPort? = null
    private var inputStream: InputStream? = null
    private var outputStream: OutputStream? = null
    private var connect: Boolean = false
    var cmd: ArrayBlockingQueue<ByteArray> = ArrayBlockingQueue(10) //队列 每次最多处理10条数据
    fun initSerialPort(p: String, rate: Int, callback: (ByteArray) -> Unit): Boolean {
        if (connect) {
            close()
        }
        try {
            val file = File(p)
            if (!file.exists()) {
                Speech.speak("串口设备不存在 请前往配置界面配置串口设备")
                TipsToast.showTips("请前往配置界面配置串口设备")
                return connect
            }
            serialPort = SerialPort(file, rate, 8, 0, 1, 0)
            inputStream = serialPort?.inputStream
            outputStream = serialPort?.outputStream
            connect = inputStream != null && outputStream != null
            println("SerialPort opened$connect")
            if (connect) {
                startReading(callback)
                startSend()
            }
            return connect
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return false
    }

    private fun startReading(callback: (ByteArray) -> Unit) {
        if (!connect) {
            return
        }
        receiverTask = Reading(false, callback)
        Thread(receiverTask).start()
    }

    private fun startSend() {
        if (!connect) {
            return
        }
        sendTask = Task(false)
        Thread(sendTask).start()
    }

    inner class Task(var interrupt: Boolean) : Runnable {
        override fun run() {
            try {
                while (!interrupt) {
                    val data = cmd.take()
                    if (data.size > 1) {
                        outputStream?.write(data)
                        println(data.hexStr())
                    }
                    Thread.sleep(999)  // 防止占用过多CPU
                }
                Speech.speak("串口设备已断开")
                close()
            } catch (e: Exception) {
                e.printStackTrace()
                close()
            }
        }
    }

    inner class Reading(var interrupt: Boolean, val callback: (ByteArray) -> Unit) : Runnable {
        override fun run() {
            try {
                val buffer = ByteArray(1024)
                while (!interrupt) {
                    val length = inputStream?.read(buffer) ?: 0
                    if (length > 0) {
                        val data = buffer.copyOf(length)
                        callback(data)
                    }
                    // TODO: 一秒钟读取一次数据 buffer是否包含多个数据包 解析时需要注意
                    Thread.sleep(999)  // 防止占用过多CPU
                }
            } catch (e: Exception) {
                e.printStackTrace()
                close()
            }
        }
    }

    private fun ByteArray.hexStr(): String {
        return joinToString("") { "%02X".format(it) }
    }

    fun execCmd(data: ByteArray) {
        if (!connect) {
            return
        }
        cmd.offer(data)
    }

    fun close() {
        connect = false
        try {
            receiverTask?.interrupt = true
            sendTask?.interrupt = true
            cmd.offer(byteArrayOf(0))
            inputStream?.close()
            outputStream?.close()
            serialPort?.close()
            connect = false
            serialPort = null
            inputStream = null
            outputStream = null
            println("SerialPort closed")
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}
